package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.WmNewsMessageConstants;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.NewsAuthDto;
import com.heima.model.wemedia.dtos.Vo;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsCheckService;
import com.heima.wemedia.service.WmNewsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    @Autowired
    private WmNewsMapper wmNewsMapper;
    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    @Autowired
    private WmNewsCheckService wmNewsCheckService;
    @Autowired
    private WmUserMapper wmUserMapper;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    /**
     * 查询自媒体文章内容
     * @param dto
     * @return
     */
    @Override
    public ResponseResult newsList(WmNewsPageReqDto dto) {
        //1.校验参数是否存在
        if (dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2.创建查询条件对象
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        //3.封装查询条件
        //3.1状态查询
        if (dto.getStatus() != null){
            queryWrapper.eq(WmNews::getStatus,dto.getStatus());
        }
        //3.2关键字查询
        if (StringUtils.isNotBlank(dto.getKeyword())){
            queryWrapper.like(WmNews::getTitle,dto.getKeyword());
        }
        //3.3频道查询
        if (dto.getChannelId() != null){
            queryWrapper.eq(WmNews::getChannelId,dto.getChannelId());
        }
        //3.4发布时间查询
        if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null){
            queryWrapper.between(WmNews::getPublishTime,dto.getBeginPubDate(),dto.getEndPubDate());
        }
        //3.5用户查询
        queryWrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId());

        //4.执行分页查询
        Page<WmNews> wmNewsPage = wmNewsMapper.selectPage(new Page<>(dto.getPage(), dto.getSize()), queryWrapper);

        //5.返回分页查询结果
        PageResponseResult result = new PageResponseResult(dto.getPage(), dto.getSize(), (int) wmNewsPage.getTotal());
        result.setData(wmNewsPage.getRecords());

        return result;
    }

    /**
     * 发布文章或保存草稿或修改文章
     * @param dto
     * @return
     */
    @Override
    public ResponseResult submit(WmNewsDto dto) {
        //1.校验参数是否存在,文章内容不能为空或文章标题不能为空
        if (StringUtils.isBlank(dto.getContent()) || StringUtils.isBlank(dto.getTitle())){
            return ResponseResult.okResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        //2.封装数据：将前端传递到服务端的数据封装到实体类对象上
        //2.1创建实体类对象封装数据
        WmNews wmNews = new WmNews();
        //2.2复制数据(只能复制字段名一致的属性的数据值)：type（布局类型）字段、 images(f封面图片)字段不一致
        BeanUtils.copyProperties(dto,wmNews);
        //2.3获取抽取图片内容
        //2.3.1抽取内容中的图片
        //获取内容
        List<Map> contentList = JSON.parseArray(dto.getContent(), Map.class);
        //遍历得到内容中的图片
        List<String> contentImages = contentList.stream().filter(map -> "image".equals(map.get("type")))
                .map(map -> map.get("value").toString()).collect(Collectors.toList());
        //2.3.2抽取封面图片
        List<String> coverImages = dto.getImages();
        //2.4根据用户选择的自动页面布局类型封装封面图片数量,type: 0 无图，1 单图，3 多图， -1 自动
        if (dto.getType() == -1){
            //用户选择自动布局类型，则封面图片的数量根据内容中的内容图片数量判断
            if (contentImages.size() >= 3){
                //内容图片数量大于三，则封面图片为内容图片的前三个
                coverImages = contentImages.stream().limit(3).collect(Collectors.toList());
                //封装封面图片的类型数据:3 多图
                wmNews.setType((short)3);
            }else if (contentImages.size()>0){
                // //内容图片数量大于0,小于3，则封面图片为内容图片的第一个
                coverImages = contentImages.stream().limit(1).collect(Collectors.toList());
                //封装封面图片的类型数据 1 单图
                wmNews.setType((short)1);
            }else {
                //内容图片为0
                coverImages = Collections.EMPTY_LIST;
                wmNews.setType((short)0);
            }
        }
        //2.5封装封面图片数据
        if (CollectionUtils.isNotEmpty(coverImages)){
            //存在封面图片数据(StringUtils.join:可以将集合中的每个元素转换成字符串)
            wmNews.setImages(StringUtils.join(coverImages,","));
        }

        //3.新增文章数据或修改文章数据（调用同一个方法：将发布的文章保存到自媒体数据库）
        saveOrUpdateArticle(wmNews);

        //4.保存素材和文章的关联数据到关联关系表中
        //4.1保存文章和内容素材的关系
        saveContentImageRelation(wmNews,contentImages);
        //4.2保存文章和封面素材的关系
        saveCoverImageRelation(wmNews,coverImages);

        //调用审核服务对文章进行审核
        wmNewsCheckService.checkNews(wmNews.getId());

        return ResponseResult.okResult("文章发布成功！");
    }

    /**
     * 根据文章id查询文章详情
     * @param newsId
     * @return
     */
    @Override
    public ResponseResult one(Integer newsId) {
        WmNews wmNews = wmNewsMapper.selectById(newsId);
        return ResponseResult.okResult(wmNews);
    }

    /**
     * 自媒体文章上架和下架功能
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
        //1.校验参数
        if (dto==null || dto.getEnable() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2.根据id查询文章
        WmNews wmNews = wmNewsMapper.selectById(dto.getId());
        //3.判断文章是否存在
        if (wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //4.判断怒文章是否发布
        if (wmNews.getStatus() != WmNews.Status.PUBLISHED.getCode()){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章未上架或下架，无法执行上下架操作");
        }
        //5.存在，修改文章的上下架状态（
        if (dto.getEnable()!=0 && dto.getEnable()!=1){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"参数错误");
        }
        wmNews.setEnable(dto.getEnable());
        //跟新数据
        wmNewsMapper.updateById(wmNews);

        //6.通过kafka发送消息,像App端发送文章上架或下架的消息
        //6.1封装需要发送的消息
        Map<String,String> map = new HashMap<>();
        map.put("articleId",wmNews.getArticleId().toString());
        map.put("enable",dto.getEnable().toString());

        kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSON.toJSONString(map));

        return ResponseResult.okResult("文章上下架成功！");
    }

    /**
     * 后台查询文章
     * @param newsAuthDto
     * @return
     */
    @Override
    public ResponseResult findHumanReview(NewsAuthDto newsAuthDto) {
        //1.参数校验
        if (newsAuthDto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        //2.创建查询条件
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        //3.创建分页对象
        Page<WmNews> page = new Page<>(newsAuthDto.getPage(),newsAuthDto.getSize());

        if (StringUtils.isNotBlank(newsAuthDto.getTitle())){
            queryWrapper.eq(WmNews::getTitle,newsAuthDto.getTitle());
        }
        if (newsAuthDto.getStatus() != null){
            queryWrapper.eq(WmNews::getStatus,newsAuthDto.getStatus());
        }
        Page<WmNews> wmNewsPage = wmNewsMapper.selectPage(page, queryWrapper);

        List<WmNews> records = wmNewsPage.getRecords();
        List<Vo> vos = records.stream().map(record -> {
            Vo vo = new Vo();
            Long articleId = record.getArticleId();
            BeanUtils.copyProperties(record, vo);
            WmUser wmUser = wmUserMapper.selectById(record.getUserId());
            if (wmUser != null) {
                vo.setAuthorName(wmUser.getName());
            }
            return vo;
        }).collect(Collectors.toList());
        PageResponseResult result = new PageResponseResult(newsAuthDto.getPage(),newsAuthDto.getSize(), (int) wmNewsPage.getTotal());
        result.setData(vos);

        return result;
    }

    /**
     * 后台管理员根据id查询文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult findWmNewsById(Integer id) {
        if (id==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        WmNews wmNews = wmNewsMapper.selectById(id);
        //获取文章中的用户id,查询作者名称
        Integer userId = wmNews.getUserId();
        WmUser wmUser = wmUserMapper.selectById(userId);
        if (wmUser == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        Vo vo = new Vo();
        BeanUtils.copyProperties(wmNews,vo);
        vo.setAuthorName(wmUser.getName());
        return ResponseResult.okResult(vo);
    }

    /**
     * 后台管理员审核文章
     * @param newsAuthDto
     * @return
     */
    @Override
    public ResponseResult humanReviewNews(NewsAuthDto newsAuthDto) {
        //1.校验参数
        if (newsAuthDto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (newsAuthDto.getId()==null && newsAuthDto.getStatus()==null && newsAuthDto.getId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2.查询文章详情
        WmNews wmNews = wmNewsMapper.selectById(newsAuthDto.getId());
        if (wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.判断审核的类型
        if (newsAuthDto.getStatus() == 4){
            //人工审核为通过
            wmNews.setStatus((short)9);
            wmNews.setReason("文章审核通过！");
            wmNewsMapper.updateById(wmNews);
        }else if (newsAuthDto.getStatus() == 2){
            //人工审核失败
            wmNews.setStatus((short)2);
            wmNews.setReason(newsAuthDto.getMsg());
            wmNewsMapper.updateById(wmNews);
        }else {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        return ResponseResult.okResult("人工审核完成！");
    }


    //===========================================================================================
    /**
     * 保存文章封面图片和素材的引用关系到关系表
     * @param wmNews
     * @param coverImages
     */
    private void saveCoverImageRelation(WmNews wmNews, List<String> coverImages) {
        //1.参数校验
        if (CollectionUtils.isEmpty(coverImages)){
            //不存在素材图片
            return;
        }
        //保存文章和封面图片素材到关系表中 （还需判断引用素材的类型：0 内容引用 1：封面引用）
        saveImageRelation(wmNews,coverImages,(short)1);
    }

    /**
     * 保存文章内容图片和素材的引用关系到关系表
     * @param wmNews
     * @param contentImages
     */
    private void saveContentImageRelation(WmNews wmNews, List<String> contentImages) {
        //1.参数校验
        if (CollectionUtils.isEmpty(contentImages)){
            return;
        }
        //保存文章和内容图片素材到关系表中 （还需判断引用素材的类型：0 内容引用 1：封面引用）
        saveImageRelation(wmNews,contentImages,(short)0);
    }

    /**
     * 保存文章和图片素材引用关系的方法
     * @param wmNews
     * @param images
     * @param type
     */
    private void saveImageRelation(WmNews wmNews, List<String> images, short type) {
        //1.判断文章是否是草稿（类型为0，则不用保存引用关系）
        if (wmNews.getStatus() == 0){
            return;
        }

        //2.根据URL获取素材ID
        //2.1对素材url去重，去掉重复的素材
        images = images.stream().distinct().collect(Collectors.toList());
        //2.2查询素材列表
        LambdaQueryWrapper<WmMaterial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(WmMaterial::getUrl,images);
        List<WmMaterial> wmMaterialList = wmMaterialMapper.selectList(queryWrapper);
        //2.3提取素材id
        List<Integer> imageIds = wmMaterialList.stream().map(wmMaterial -> wmMaterial.getId()).collect(Collectors.toList());
        //2.4判断素材是否缺失
        if (images.size() > imageIds.size()){
            throw new RuntimeException("素材数据丢失");
        }
        //3.保存关联数据到关联表中
        wmNewsMaterialMapper.saveNewsMaterial(imageIds,wmNews.getId(),type);

    }

    /**
     * 新增文章或修改文章的方法
     * @param wmNews
     */
    private void saveOrUpdateArticle(WmNews wmNews) {
        //0.封装修改和新增都需要的数据
        wmNews.setSubmitedTime(new Date());
        wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
        wmNews.setEnable((short)1);
        //1.判断是新增还是修改文章（根据文章对象是否存在id进行判断）
        Integer id = wmNews.getId();
        if (id == null){
            //2.新增文章，创建封装数据
            wmNews.setCreatedTime(new Date());
            //保存到数据库中
            wmNewsMapper.insert(wmNews);
            return;
        }
        //3.修改文章数据
        //3.1修改数据库中的数据
        wmNewsMapper.updateById(wmNews);
        //3.2文章被修改后，需要删除文章和素材的引用关系
        LambdaQueryWrapper<WmNewsMaterial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WmNewsMaterial::getNewsId,wmNews.getId());
        wmNewsMaterialMapper.delete(queryWrapper);
    }
}
